001    /*
002     * CrochemoreLandauZivUkelsonLocalAlignment.java
003     *
004     * Copyright 2003 Sergio Anibal de Carvalho Junior
005     *
006     * This file is part of NeoBio.
007     *
008     * NeoBio is free software; you can redistribute it and/or modify it under the terms of
009     * the GNU General Public License as published by the Free Software Foundation; either
010     * version 2 of the License, or (at your option) any later version.
011     *
012     * NeoBio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
013     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
014     * PURPOSE. See the GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License along with NeoBio;
017     * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
018     * Boston, MA 02111-1307, USA.
019     *
020     * Proper attribution of the author as the source of the software would be appreciated.
021     *
022     * Sergio Anibal de Carvalho Junior             mailto:sergioanibaljr@users.sourceforge.net
023     * Department of Computer Science               http://www.dcs.kcl.ac.uk
024     * King's College London, UK                    http://www.kcl.ac.uk
025     *
026     * Please visit http://neobio.sourceforge.net
027     *
028     * This project was supervised by Professor Maxime Crochemore.
029     *
030     */
031    
032    package neobio.alignment;
033    
034    /**
035     * This class implements the <B>local</B> pairwise sequence alignment algorithm (with
036     * linear gap penalty function) due to Maxime Crochemore, Gad Landau and Michal
037     * Ziv-Ukelson (2002).
038     *
039     * <P>This implementation derives from the paper of M.Crochemore, G.Landau and
040     * M.Ziv-Ukelson, <I>A Sub-quadratic Sequence Alignment Algorithm for Unrestricted Scoring
041     * Matrices</I> (available here as
042     * <A HREF="doc-files/Crochemore_Landau_Ziv-Ukelson_algorithm.pdf">PDF</A> or
043     * <A HREF="doc-files/Crochemore_Landau_Ziv-Ukelson_algorithm.pdf">Postscript</A>).</P>
044     *
045     * <P>For a general description of the algorithm, please refer to the specification of the
046     * abstract {@linkplain CrochemoreLandauZivUkelson} superclass.</P>
047     *
048     * <P>This class consist mainly of methods that:</P>
049     *
050     * <LU>
051     * <LI>create and compute all information of a block (see {@link #createBlock createBlock}
052     * and its variants);
053     * <LI>compute the output border of a block (see {@link #computeOutputBorder
054     * computeOutputBorder};
055     * <LI>locate the score of a high scoring global alignment in the block table (see {@link
056     * #locateScore locateScore};
057     * <LI>build an optimal global alignment from the information stored in the block table
058     * (see {@link #buildOptimalAlignment buildOptimalAlignment}.
059     * </LU>
060     *
061     * <P>This algorithm works essentially in the same way as the global alignment version.
062     * The main differences is that an aptimal path can either be contained entirely in a
063     * block (called <B>C-path</B>) or be a block-crossing path. A block-crossing path
064     * consists of a (possibly empty) <B>S-path</B> (a path that starts inside a block and
065     * ends in its output border), followed by any number of paths that cross a block from its
066     * input border to its output border, and ending in an <B>E-path</B> (a path that starts
067     * in the input border of a block and ends inside the block).</P>
068     *
069     * <P>Therefore, it is necessary to compute extra information to keep track of these
070     * possibilities. This is accomplished by using an instance of a {@linkplain
071     * LocalAlignmentBlock} (which extends the {@linkplain AlignmentBlock} class) for every
072     * block in the block table.</P>
073     *
074     * @see CrochemoreLandauZivUkelson
075     * @see CrochemoreLandauZivUkelsonLocalAlignment
076     * @author Sergio A. de Carvalho Jr.
077     */
078    public class CrochemoreLandauZivUkelsonLocalAlignment extends CrochemoreLandauZivUkelson
079    {
080            /**
081             * A constant that indicates that the best path ending at a given entry of the output
082             * border is a block-crossing path (one that starts outside the block).
083             */
084            protected static final byte TYPE_CROSSING_PATH = 0;
085    
086            /**
087             * A constant that indicates that the best path ending at a given entry of the output
088             * border is a S-path (one that starts inside the block).
089             */
090            protected static final byte TYPE_S_PATH = 1;
091    
092            /**
093             * A constant that indicates that the high scoring path ending in a given block is a
094             * C-path, i.e. one that starts inside the block.
095             */
096            protected static final byte TYPE_C_PATH = 2;
097    
098            /**
099             * A constant that indicates that the high scoring path ending in a given block is an
100             * E-path, i.e. one that starts at its input border.
101             */
102            protected static final byte TYPE_E_PATH = 3;
103    
104            /**
105             * The score of the high scoring local alignment found.
106             */
107            protected int max_score;
108    
109            /**
110             * The row index of a block (in the block table) where the high scoring local
111             * alignment ends.
112             */
113            protected int max_row;
114    
115            /**
116             * The column index of a block (in the block table) where the high scoring local
117             * alignment ends.
118             */
119            protected int max_col;
120    
121            /**
122             * The type of the high scoring local alignment found.
123             */
124            protected byte max_path_type;
125    
126            /**
127             * If the high scoring local alignment ends in an E-path at a block B, this field
128             * contains the index of the entry in the input border of B that where the E-path
129             * starts.
130             */
131            protected int max_source_index;
132    
133            /**
134             * Creates and computes all information of an alignment block. This method works
135             * essentially in the same way as its global alignment counterpart. Its main job is to
136             * compute the DIST column for the block. It then request the
137             * <CODE>computeOutputBorder</CODE> method to compute the block's output border. It
138             * also computes all S, C and E-paths of this block. Finally, it checks if the C-path
139             * of this block is higher than the highest score found so far.
140             *
141             * @param factor1 factor of the first sequence
142             * @param factor2 factor of the second sequence
143             * @param row row index of the block in the block table
144             * @param col column index of the block in the block table
145             * @return the computed block
146             * @throws IncompatibleScoringSchemeException if the scoring scheme is not compatible
147             * with the sequences being aligned
148             */
149            protected AlignmentBlock createBlock (Factor factor1, Factor factor2, int row,
150                    int col) throws IncompatibleScoringSchemeException
151            {
152                    LocalAlignmentBlock     block, left_prefix, diag_prefix, top_prefix;
153                    int                                     size, lr, lc, max, ins_E, del_E;
154                    int                                     score_ins, score_sub, score_del, ins, del, sub;
155    
156                    lr = factor1.length();
157                    lc = factor2.length();
158                    size = lr + lc + 1;
159    
160                    block = new LocalAlignmentBlock (factor1, factor2, size);
161    
162                    // retrieve pointers to prefixes
163                    left_prefix = (LocalAlignmentBlock) getLeftPrefix (block);
164                    diag_prefix = (LocalAlignmentBlock) getDiagonalPrefix (block);
165                    top_prefix  = (LocalAlignmentBlock) getTopPrefix (block);
166    
167                    // compute scores
168                    score_ins = scoreInsertion (factor2.getNewChar());
169                    score_sub = scoreSubstitution (factor1.getNewChar(), factor2.getNewChar());
170                    score_del = scoreDeletion (factor1.getNewChar());
171    
172                    // compute block's data
173                    for (int i = 0; i < size; i++)
174                    {
175                            ins = sub = del = ins_E = del_E = Integer.MIN_VALUE;
176    
177                            if (i < size - 1)
178                            {
179                                    ins = left_prefix.dist_column[i] + score_ins;
180                                    ins_E = left_prefix.E_path_score[i];
181                            }
182    
183                            if ((i > 0) && (i < size - 1))
184                            {
185                                    sub = diag_prefix.dist_column[i - 1] + score_sub;
186                            }
187    
188                            if (i > 0)
189                            {
190                                    del = top_prefix.dist_column[i - 1] + score_del;
191                                    del_E = top_prefix.E_path_score[i - 1];
192                            }
193    
194                            block.dist_column[i] = max = max (ins, sub, del);
195    
196                            if (max == ins)
197                                    block.direction[i] = LEFT_DIRECTION;
198                            else if (max == sub)
199                                    block.direction[i] = DIAGONAL_DIRECTION;
200                            else
201                                    block.direction[i] = TOP_DIRECTION;
202    
203                            block.E_path_score[i] = max = max (ins_E, block.dist_column[i], del_E);
204    
205                            if (max == ins_E)
206                            {
207                                    block.E_path_ancestor[i] = left_prefix.E_path_ancestor[i];
208                                    block.E_path_ancestor_index[i] = left_prefix.E_path_ancestor_index[i];
209                            }
210                            else if (max == block.dist_column[i])
211                            {
212                                    block.E_path_ancestor[i] = block;
213                                    block.E_path_ancestor_index[i] = i;
214                            }
215                            else
216                            {
217                                    block.E_path_ancestor[i] = top_prefix.E_path_ancestor[i - 1];
218                                    block.E_path_ancestor_index[i] = top_prefix.E_path_ancestor_index[i - 1];
219                            }
220    
221                            if (i < lc)
222                            {
223                                    block.S_path_score[i] = left_prefix.S_path_score[i];
224                            }
225                            else if (i == lc)
226                            {
227                                    ins = left_prefix.S_path_score[i-1] + score_ins;
228                                    sub = diag_prefix.S_path_score[i-1] + score_sub;
229                                    del = top_prefix.S_path_score[i]    + score_del;
230    
231                                    block.S_path_score[i] = max = max (0, ins, sub, del);
232    
233                                    if (max == ins)
234                                            block.S_direction = LEFT_DIRECTION;
235                                    else if (max == sub)
236                                            block.S_direction = DIAGONAL_DIRECTION;
237                                    else if (max == del)
238                                            block.S_direction = TOP_DIRECTION;
239                                    else
240                                            block.S_direction = STOP_DIRECTION;
241                            }
242                            else
243                            {
244                                    block.S_path_score[i] = top_prefix.S_path_score[i - 1];
245                            }
246                    }
247    
248                    computeOutputBorder (block, row, col, size, lc, lr);
249    
250                    ins = left_prefix.C;
251                    del = top_prefix.C;
252                    block.C = max = max (ins, block.S_path_score[lc], del);
253    
254                    if (block.C > max_score)
255                    {
256                            // assert block.C == block.S_path_score[lc]; => always true
257                            max_score = block.C;
258                            max_row = row;
259                            max_col = col;
260                            max_path_type = TYPE_C_PATH;
261                    }
262    
263                    return block;
264            }
265    
266            /**
267             * Creates the root block. This is a special case of the <CODE>createBlock</CODE>
268             * method. No information is actually computed.
269             *
270             * @param factor1 factor of the first sequence
271             * @param factor2 factor of the second sequence
272             * @return the root block
273             */
274            protected AlignmentBlock createRootBlock (Factor factor1, Factor factor2)
275            {
276                    // resets the variables that keep track
277                    // of the high scoring alignment
278                    max_row = max_col = max_score = 0;
279                    max_path_type = TYPE_C_PATH;
280    
281                    return new LocalAlignmentBlock (factor1, factor2);
282            }
283    
284            /**
285             * Creates and computes all information of an alignment block of the first column of
286             * the block table. This is a special case of the <CODE>createBlock</CODE> method.
287             *
288             * @param factor1 factor of the first sequence
289             * @param factor2 factor of the second sequence
290             * @param col column index of the block in the block table
291             * @return the computed block
292             * @throws IncompatibleScoringSchemeException if the scoring scheme is not compatible
293             * with the sequences being aligned
294             * @see #createBlock createBlock
295             */
296            protected AlignmentBlock createFirstRowBlock (Factor factor1, Factor factor2, int col)
297                    throws IncompatibleScoringSchemeException
298            {
299                    LocalAlignmentBlock     block, left_prefix;
300                    int                     size, lr, lc, score_ins;
301    
302                    lr = 0; // factor1.length();
303                    lc = factor2.length();
304                    size = lr + lc + 1;
305    
306                    block = new LocalAlignmentBlock (factor1, factor2, size);
307    
308                    // retrieve a pointer to left prefix
309                    left_prefix = (LocalAlignmentBlock) getLeftPrefix (block);
310    
311                    // compute insertion's score
312                    score_ins = scoreInsertion (factor2.getNewChar());
313    
314                    // compute block's data
315                    for (int i = 0; i < lc; i++)
316                    {
317                            block.dist_column[i] = left_prefix.dist_column[i] + score_ins;
318                            block.direction[i] = LEFT_DIRECTION;
319                            block.S_path_score[i] = left_prefix.S_path_score[i];
320    
321                            block.E_path_score[i] = left_prefix.E_path_score[i];
322                            block.E_path_ancestor[i] = left_prefix.E_path_ancestor[i];
323                            block.E_path_ancestor_index[i] = left_prefix.E_path_ancestor_index[i];
324                            if (block.dist_column[i] > block.E_path_score[i])
325                            {
326                                    block.E_path_score[i] = block.dist_column[i];
327                                    block.E_path_ancestor[i] = block;
328                                    block.E_path_ancestor_index[i] = i;
329                            }
330                    }
331    
332                    // last position
333                    block.E_path_score[lc] = block.dist_column[lc] = 0;
334                    block.direction[lc] = STOP_DIRECTION;
335    
336                    block.E_path_ancestor[lc] = block;
337                    block.E_path_ancestor_index[lc] = lc;
338    
339                    block.S_direction = LEFT_DIRECTION;
340                    block.S_path_score[lc] = left_prefix.S_path_score[lc - 1] + score_ins;
341                    if (block.S_path_score[lc] <= 0)
342                    {
343                            block.S_path_score[lc] = 0;
344                            block.S_direction = STOP_DIRECTION;
345                    }
346    
347                    computeOutputBorder (block, 0, col, size, lc, lr);
348    
349                    block.C = max (left_prefix.C, block.S_path_score[lc]);
350    
351                    if (block.C > max_score)
352                    {
353                            max_score = block.C;
354                            max_row = 0;
355                            max_col = col;
356                            max_path_type = TYPE_C_PATH;
357                    }
358    
359                    return block;
360            }
361    
362            /**
363             * Creates and computes all information of an alignment block of the first column of
364             * the block table. This is a special case of the <CODE>createBlock</CODE> method.
365             *
366             * @param factor1 factor of the first sequence
367             * @param factor2 factor of the second sequence
368             * @param row row index of the block in the block table
369             * @return the computed block
370             * @throws IncompatibleScoringSchemeException if the scoring scheme is not compatible
371             * with the sequences being aligned
372             * @see #createBlock createBlock
373             */
374            protected AlignmentBlock createFirstColumnBlock (Factor factor1, Factor factor2,
375                    int row) throws IncompatibleScoringSchemeException
376            {
377                    LocalAlignmentBlock     block, top_prefix;
378                    int                     size, lr, lc, score_del;
379    
380                    lr = factor1.length();
381                    lc = 0; // factor2.length();
382                    size = lr + lc + 1;
383    
384                    block = new LocalAlignmentBlock (factor1, factor2, size);
385    
386                    // retrieve a pointer to top prefix
387                    top_prefix  = (LocalAlignmentBlock) getTopPrefix (block);
388    
389                    // compute deletion's score
390                    score_del = scoreDeletion (factor1.getNewChar());
391    
392                    // first position
393                    block.E_path_score[0] = block.dist_column[0] = 0;
394                    block.direction[0] = STOP_DIRECTION;
395    
396                    block.E_path_ancestor[0] = block;
397                    block.E_path_ancestor_index[0] = 0;
398    
399                    block.S_direction = TOP_DIRECTION;
400                    block.S_path_score[0] = top_prefix.S_path_score[0] + score_del;
401                    if (block.S_path_score[0] <= 0)
402                    {
403                            block.S_path_score[0] = 0;
404                            block.S_direction = STOP_DIRECTION;
405                    }
406    
407                    // compute block's data
408                    for (int i = 1; i < size; i++)
409                    {
410                            block.dist_column[i] = top_prefix.dist_column[i - 1] + score_del;
411                            block.direction[i] = TOP_DIRECTION;
412                            block.S_path_score[i] = top_prefix.S_path_score[i - 1];
413    
414                            block.E_path_score[i] = top_prefix.E_path_score[i - 1];
415                            block.E_path_ancestor[i] = top_prefix.E_path_ancestor[i - 1];
416                            block.E_path_ancestor_index[i] = top_prefix.E_path_ancestor_index[i - 1];
417                            if (block.dist_column[i] > block.E_path_score[i])
418                            {
419                                    block.E_path_score[i] = block.dist_column[i];
420                                    block.E_path_ancestor[i] = block;
421                                    block.E_path_ancestor_index[i] = i;
422                            }
423                    }
424    
425                    computeOutputBorder (block, row, 0, size, lc, lr);
426    
427                    block.C = max (block.S_path_score[lc], top_prefix.C);
428    
429                    if (block.C > max_score)
430                    {
431                            max_score = block.C;
432                            max_row = row;
433                            max_col = 0;
434                            max_path_type = TYPE_C_PATH;
435                    }
436    
437                    return block;
438            }
439    
440            /**
441             * Computes the output border of a block. This method works essentially in the same
442             * way as its global alignment counterpart:
443             *
444             * <LU>
445             * <LI>Retrieve the block's input border;
446             * <LI>Retrieve the block's complete DIST matrix;
447             * <LI>Create an interface to the {@linkplain OutMatrix OUT} matrix from the input
448             * border and DIST matrix;
449             * <LI>Use {@linkplain Smawk SMAWK} to compute all column maxima of the OUT matrix
450             * (SMAWK finds the index of the row that contains the maximum value of a column);
451             * <LI>Assemble the output border by extracting the maximum values of each column of
452             * the OUT matrix using the information obtained in the previous step.
453             * </LU>
454             *
455             * <P>However, it also check if there is a better path starting inside the block (an
456             * S path) and oupdate the output border accordingly. It also checks if this block has
457             * any path of score higher than the maximum score found so far.
458             *
459             * @param block the block for which the output border is to be computed
460             * @param row row index of the block in the block table
461             * @param col column index of the block in the block table
462             * @param dim dimension of the output border
463             * @param lc number of columns of the block
464             * @param lr number of row of the block
465             */
466            protected void computeOutputBorder (LocalAlignmentBlock block, int row, int col, int
467                    dim, int lc, int lr)
468            {
469                    int[] input = assembleInputBorder (dim, row, col, lr);
470    
471                    int[][] dist = assembleDistMatrix (block, dim, row, col, lc); // (AlignmentBlock)
472    
473                    // build an interface to the OUT matrix
474                    out_matrix.setData (dist, input, dim, lc);
475    
476                    // compute source_path using SMAWK
477                    smawk.computeColumnMaxima(out_matrix, block.source_path);
478    
479                    // update output border
480                    for (int i = 0; i < dim; i++)
481                    {
482                            block.path_type[i] = TYPE_CROSSING_PATH;
483                            block.output_border[i] = out_matrix.valueAt(block.source_path[i], i);
484    
485                            // check if there is a better path starting inside the block
486                            // (if there is a path of equal score, preference is given
487                            // to the S-path because it ends sooner)
488                            if (block.S_path_score[i] >= block.output_border[i])
489                            {
490                                    block.output_border[i] = block.S_path_score[i];
491                                    block.path_type[i] = TYPE_S_PATH;
492                            }
493    
494                            // check if this block contains a score higher
495                            // than the best path found so far
496                            if (input[i] + block.E_path_score[i] > max_score)
497                            {
498                                    max_score = input[i] + block.E_path_score[i];
499                                    max_row = row;
500                                    max_col = col;
501                                    max_source_index = i;
502                                    max_path_type = TYPE_E_PATH;
503                            }
504                    }
505            }
506    
507            /**
508             * Builds an optimal local alignment between the loaded sequences after the block
509             * table has been computed by tracing a path back in the block table.
510             *
511             * @return an optimal global alignment
512             * @throws IncompatibleScoringSchemeException If the scoring scheme is not compatible
513             * with the loaded sequences.
514             * @see CrochemoreLandauZivUkelson#traverseBlock
515             */
516            protected PairwiseAlignment buildOptimalAlignment ()
517                    throws IncompatibleScoringSchemeException
518            {
519                    LocalAlignmentBlock     block;
520                    StringBuffer            gapped_seq1, tag_line, gapped_seq2;
521    
522                    gapped_seq1     = new StringBuffer();
523                    tag_line        = new StringBuffer();
524                    gapped_seq2     = new StringBuffer();
525    
526                    block = (LocalAlignmentBlock) block_table[max_row][max_col];
527    
528                    if (max_path_type == TYPE_C_PATH)
529                    {
530                            // a C-path is essentially an S-path
531                            traverseS_Path (block, gapped_seq1, tag_line, gapped_seq2);
532                    }
533                    else
534                    {
535                            traverseBlockCrossingPath (block, gapped_seq1, tag_line, gapped_seq2);
536                    }
537    
538                    return new PairwiseAlignment (gapped_seq1.toString(), tag_line.toString(),
539                            gapped_seq2.toString(), locateScore());
540            }
541    
542            /**
543             * Traverses a series of block crossing paths to retrieve an optimal alignment. A
544             * block-crossing path consists of a (possibly empty) <B>S-path</B> (a path that
545             * starts inside a block and ends in its output border), followed by any number of
546             * paths that cross a block from its input border to its output border, and ending in
547             * an <B>E-path</B> (a path that starts in the input border of a block and ends inside
548             * the block).
549             *
550             * @param block the block to be traversed
551             * @param gapped_seq1 the StringBuffer to where the gapped sequence 1 is written to
552             * @param tag_line the StringBuffer to where the tag_line is written to
553             * @param gapped_seq2 the StringBuffer to where the gapped sequence 2 is written to
554             * @throws IncompatibleScoringSchemeException If the scoring scheme is not compatible
555             * with the loaded sequences.
556             */
557            protected void traverseBlockCrossingPath (LocalAlignmentBlock block,
558                    StringBuffer gapped_seq1, StringBuffer tag_line, StringBuffer gapped_seq2)
559                    throws IncompatibleScoringSchemeException
560            {
561                    LocalAlignmentBlock     ancestor;
562                    int                                     source, dest, ancestor_source;
563                    int                                     row, col;
564    
565                    row = max_row;
566                    col = max_col;
567    
568                    // recover the E-path
569                    source = max_source_index;
570                    ancestor = block.E_path_ancestor[source];
571                    ancestor_source = block.E_path_ancestor_index[source];
572                    traverseBlock (ancestor, ancestor_source, gapped_seq1, tag_line, gapped_seq2);
573    
574                    // now recover crossing paths
575                    while (true)
576                    {
577                            if (row == 0)
578                            {
579                                    col = col - 1;
580                                    dest = block_table[row][col].factor2.length();
581                            }
582                            else if (col == 0)
583                            {
584                                    row = row - 1;
585                                    dest = 0;
586                            }
587                            else
588                            {
589                                    if (source < block.factor1.length())
590                                    {
591                                            col = col - 1;
592                                            dest = block_table[row][col].factor2.length() + source;
593                                    }
594                                    else if (source == block.factor1.length())
595                                    {
596                                            row = row - 1; col = col - 1;
597                                            dest = block_table[row][col].factor2.length();
598                                    }
599                                    else
600                                    {
601                                            row = row - 1;
602                                            dest = source - block.factor1.length();
603                                    }
604                            }
605    
606                            // check if has reached the root block
607                            if (!(row > 0 || col > 0)) break;
608    
609                            block = (LocalAlignmentBlock) block_table[row][col];
610    
611                            if (block.path_type[dest] == TYPE_S_PATH)
612                            {
613                                    // last part, an S-path, and we're done
614                                    ancestor = (LocalAlignmentBlock) block.ancestor[dest];
615                                    traverseS_Path (ancestor, gapped_seq1, tag_line, gapped_seq2);
616                                    break;
617                            }
618    
619                            source   = block.source_path[dest];
620                            ancestor = (LocalAlignmentBlock) block.ancestor[dest];
621                            ancestor_source = source;
622    
623                            if (dest > block.factor2.length())
624                                    ancestor_source -= (block.factor1.length() - ancestor.factor1.length());
625    
626                            traverseBlock (ancestor, ancestor_source, gapped_seq1, tag_line, gapped_seq2);
627                    }
628            }
629    
630            /**
631             * Traverses an S-path of a block to retrieve a part of an optimal alignment from the
632             * new vertex of a block to entry in its input border. This method is essentially
633             * similar to the <CODE>traverseBlock</CODE>. The only difference is that it uses
634             * the information of the <CODE>S_direction field</CODE> of the
635             * <CODE>LocalAlignmentBlock</CODE> class.
636             *
637             * @param block the block to be traversed
638             * @param gapped_seq1 the StringBuffer to where the gapped sequence 1 is written to
639             * @param tag_line the StringBuffer to where the tag_line is written to
640             * @param gapped_seq2 the StringBuffer to where the gapped sequence 2 is written to
641             * @throws IncompatibleScoringSchemeException If the scoring scheme is not compatible
642             * with the loaded sequences.
643             */
644            protected void traverseS_Path (LocalAlignmentBlock block, StringBuffer gapped_seq1,
645                    StringBuffer tag_line, StringBuffer gapped_seq2)
646                    throws IncompatibleScoringSchemeException
647            {
648                    char char1, char2;
649    
650                    while (block.S_direction != STOP_DIRECTION)
651                    {
652                            char1 = block.factor1.getNewChar();
653                            char2 = block.factor2.getNewChar();
654    
655                            switch (block.S_direction)
656                            {
657                                    case LEFT_DIRECTION:
658                                            gapped_seq1.insert (0, GAP_CHARACTER);
659                                            tag_line.insert (0, GAP_TAG);
660                                            gapped_seq2.insert (0, char2);
661    
662                                            block = (LocalAlignmentBlock) getLeftPrefix (block);
663                                            break;
664    
665                                    case DIAGONAL_DIRECTION:
666                                            gapped_seq1.insert (0, char1);
667                                            if (char1 == char2)
668                                                    if (useMatchTag())
669                                                            tag_line.insert (0, MATCH_TAG);
670                                                    else
671                                                            tag_line.insert (0, char1);
672                                            else if (scoreSubstitution(char1, char2) > 0)
673                                                    tag_line.insert (0, APPROXIMATE_MATCH_TAG);
674                                            else
675                                                    tag_line.insert (0, MISMATCH_TAG);
676                                            gapped_seq2.insert(0, char2);
677    
678                                            block = (LocalAlignmentBlock) getDiagonalPrefix (block);
679                                            break;
680    
681                                    case TOP_DIRECTION:
682                                            gapped_seq1.insert (0, char1);
683                                            tag_line.insert (0, GAP_TAG);
684                                            gapped_seq2.insert (0, GAP_CHARACTER);
685    
686                                            block = (LocalAlignmentBlock) getTopPrefix (block);
687                                            break;
688                            }
689                    }
690            }
691    
692            /**
693             * Returns the score of the high scoring local alignment in the block table.
694             *
695             * @return the score of the highest scoring local alignment
696             */
697            protected int locateScore ()
698            {
699                    return max_score;
700            }
701    }